# barco/srcreposd.py

import os
import threading
import SocketServer
import SimpleXMLRPCServer
import git
import time
from . import error

class Server(object):

    class ThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): pass
        
    class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): pass

    class Task(threading.Thread):

        def __init__(self, server):
            super(Server.Task, self).__init__()

            self.server = server
            self.lock = threading.Condition()
            self.done = False   

            self.server.lock.acquire()
            self.server.tasks[str(id(self))] = self
            self.server.lock.release()
     
            self.output = list()

            return None
        
        def read_output(self):
            self.lock.acquire()
            while len(self.output) == 0:
                if self.done == True: 
                    self.lock.release()
                    return list()
                self.lock.wait()
            output = self.output
            self.output = list()
            self.lock.release()
            return output

        def write_output(self, data):
            self.lock.acquire()
            self.output.append(data)
            self.lock.notify()
            self.lock.release()
            return None

    class CreateModule(Task):

        def __init__(self, server, name):  
            super(Server.CreateModule, self).__init__(server)
            self.module = name  
            self.start()
            return None
            
        def run(self):
            # TODO: check if module already exists
            # TODO: check if module is not circular
            self.write_output('Creating module \'%s\' ...' % self.module)
            self.server.module_create(self.module)
            self.write_output('done.')                
            self.done = True
            return None

    class CreateBranch(Task):

        def __init__(self, server, name):  
            super(Server.CreateBranch, self).__init__(server)
            self.branch = name 
            self.start()
            return None        
               
        def run(self): 
            # TODO: check if branch already exists
            self.write_output('Creating branch \'%s\' ...' % self.branch)
            self.server.branch_create(self.write_output, self.branch)
            self.write_output('done.')
            self.done = True
            return None

    class Functions(object):

        def __init__(self, server):
            self.server = server
            return None

        def create_module(self, name):
            return str(id(Server.CreateModule(self.server, name)))

        def create_branch(self, name):
            return str(id(Server.CreateBranch(self.server, name)))

        def read_output(self, key):
            ret = list()
            self.server.lock.acquire()
            if key in self.server.tasks: ret = self.server.tasks[key].read_output()
            self.server.lock.release()
            return ret

        def list_modules(self):
            self.server.lock.acquire()
            modules = self.server.modules.keys()
            self.server.lock.release()
            return modules

        def list_branches(self):
            self.server.lock.acquire()
            branches = map(lambda b: ''.join(b.split('.')[1:]), self.server.branches)
            self.server.lock.release()
            return branches

        def get_module_checkout_info(self, module, tag):
            ret = dict()
            ret['name'] = module
            ret['type'] = 'git'
            ret['tag'] = 'barco.' + tag
            self.server.lock.acquire()
            print module
            if self.server.module_exists(module): ret['url']  = self.server.module_to_reposdir(module)
            else: ret['url'] = None
            self.server.lock.release()
            return ret

    def __init__(self):

        # setup the repository directory
        install_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
        self.repository = os.path.join(install_dir, 'var', 'srcrepos')

        # Setting up the xmlrpc server
        self.server = Server.ThreadedXMLRPCServer(('0.0.0.0', 4242), Server.RequestHandler, False)
        self.server.register_instance(Server.Functions(self))
        self.server.allow_reuse_address = True

        # initialize private data
        self.modules = dict()
        self.branches = list()
        self.tasks = dict()

        # initialize mutex
        self.lock = threading.RLock()

        return None

    def set_repository(self, repository):
        self.repository = repository
        return None

    def run(self):

        # Chech if our repository is a directory
        if not os.path.isdir(self.repository):
            raise error.Error('specified repository \'%s\' is not a directory.' % self.repository)

        self.initialize_repository()

        # Start the server
        self.server.serve_forever()
        return None

    def module_to_reposdir(self, module):
        return os.path.join(self.repository, str(module))

    def module_exists(self, module):
        return git.repo.fun.is_git_dir(os.path.join(self.module_to_reposdir(module), '.git'))

    def create_sconscript(self, mrepo, mdir):
        sconscript = os.path.join(mdir, 'sconscript.py')
        f = open(sconscript, 'w')
        f.write('print hello\n')
        f.close()
        return sconscript

    def create_moduleconf(self, mrepo, mdir):
        moduleconf = os.path.join(mdir, 'moduleconf.py')
        f = open(moduleconf, 'w')
        f.close()
        return moduleconf

    def module_create(self, module, root_module=False):
        self.lock.acquire()
        mdir = self.module_to_reposdir(module)
        if not os.path.exists(mdir): os.makedirs(mdir)
        if not os.path.isdir(mdir): raise error.Error('Oops! \'%s\' is not a directory.' % mdir)
        mrepo = git.Repo.init(mdir)
        mindex = mrepo.index
        mindex.add([self.create_sconscript(mrepo, mdir)])
        mindex.add([self.create_moduleconf(mrepo, mdir)])
        mindex.commit('initial version')
        if not root_module:
            self.modules[module] = {'name': module, 'dir': mdir, 'repo': mrepo}
            for b in self.branches: 
                print b
                if b == 'barco.dev': mrepo.heads.master.rename('barco.dev')
                else: mrepo.create_head(b)
        else:
            mrepo.heads.master.rename('barco.dev')
        self.lock.release()
        return mrepo;

    def branch_create(self, progress, branch):
        self.lock.acquire()
        for m in self.modules.keys():
            if m == 'srcrepos': continue
            progress(' --> branching: %s' % m)
        progress(' --> branching: srcrepos')
        self.lock.release()       
        return 0

    def module_open(self, module):
        mrepo = git.Repo(self.module_to_reposdir(module))        
        return mrepo

    def walk_and_find_modules(self):
        ret = dict()
        for root, dirs, files in os.walk(self.repository):
            if '.git' in dirs:
                del dirs[:]                
                name = os.path.relpath(root, self.repository)
                ret[name] = {'name': name, 'dir': root, 'repo': git.Repo(root)}
        return ret

    def initialize_repository(self):
        try:            
            self.lock.acquire()
            repos_module = 'srcrepos'
            if not self.module_exists(repos_module): mrepo = self.module_create(repos_module, True)
            else: mrepo = self.module_open(repos_module)            
            # get the available branches and modules.
            self.branches = map(lambda r: r.name, mrepo.heads)
            self.modules = self.walk_and_find_modules()
            self.lock.release()
        except:
            self.lock.release()
            raise

        return None

